/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef CARINA_WINDOW_HH
#define CARINA_WINDOW_HH

#include "carina/common/global.hh"

#include <functional>

using std::function;
using std::bind;
using namespace std::placeholders;

#include "carina/common/containers.hh"

namespace Carina
{
class Window;

//! Pointer type for 
typedef shared_ptr<Window> WindowPtr;

//! Describes the attributes which were changed as part of window resize event.
class ResizeWindowEvent
{
    size_t m_Width;     //!< The new width of the window.
    size_t m_Height;    //!< The new height of the window.
public:
    /*! \brief Constructor.
     *  \param width    the new width of the window.
     *  \param height   the new height of the window.
     */
    ResizeWindowEvent(size_t width, size_t height)
        :   m_Width(width),
            m_Height(height) {}

    //! Returns the new width of the window.
    size_t getWidth() const { return m_Width; }

    //! Returns the height of the window.
    size_t getHeight() const { return m_Height; }
};

//! Enumerates the keyboard keys.
enum KeyboardKey
{
    CE_KEY_UNKNOWN,     //!< Unknown or unspecified key.
    CE_KEY_BACKSPACE,   //!< Backspace key.
    CE_KEY_TAB,         //!< Tab key.
    CE_KEY_ENTER,       //!< Enter key.
    CE_KEY_LCTRL,       //!< Left Control key
    CE_KEY_RCTRL,       //!< Right Control key.
    CE_KEY_LSHIFT,      //!< Left Shift key.
    CE_KEY_RSHIFT,      //!< Right Shift key.
    CE_KEY_LALT,        //!< Left Alt key.
    CE_KEY_RALT,        //!< Right Alt key.
    CE_KEY_LWIN,        //!< Left Windows/Super key.
    CE_KEY_RWIN,        //!< Right Windows/Super key.
    CE_KEY_PAUSE,       //!< Pause key.
    CE_KEY_CAPSLOCK,    //!< Caps Lock key.
    CE_KEY_ESCAPE,      //!< Escape key.
    CE_KEY_SPACE,       //!< Space bar.
    CE_KEY_PAGEUP,      //!< Page Up key.
    CE_KEY_PAGEDOWN,    //!< Page Down key.
    CE_KEY_END,         //!< End key.
    CE_KEY_HOME,        //!< Home key.
    CE_KEY_LEFT,        //!< Left arrow key.
    CE_KEY_RIGHT,       //!< Right arrow key.
    CE_KEY_UP,          //!< Up arrow key.
    CE_KEY_DOWN,        //!< Down arrow key.
    CE_KEY_PRINTSCREEN, //!< Print Screen key.
    CE_KEY_INSERT,      //!< Insert key.
    CE_KEY_DELETE,      //!< Delete key.
    CE_KEY_0,           //!< '0' key.
    CE_KEY_1,           //!< '1' key.
    CE_KEY_2,           //!< '2' key.
    CE_KEY_3,           //!< '3' key.
    CE_KEY_4,           //!< '4' key.
    CE_KEY_5,           //!< '5' key.
    CE_KEY_6,           //!< '6' key.
    CE_KEY_7,           //!< '7' key.
    CE_KEY_8,           //!< '8' key.
    CE_KEY_9,           //!< '9' key.
    CE_KEY_A,           //!< 'A' key.
    CE_KEY_B,           //!< 'B' key.
    CE_KEY_C,           //!< 'C' key.
    CE_KEY_D,           //!< 'D' key.
    CE_KEY_E,           //!< 'E' key.
    CE_KEY_F,           //!< 'F' key.
    CE_KEY_G,           //!< 'G' key.
    CE_KEY_H,           //!< 'H' key.
    CE_KEY_I,           //!< 'I' key.
    CE_KEY_J,           //!< 'J' key.
    CE_KEY_K,           //!< 'K' key.
    CE_KEY_L,           //!< 'L' key.
    CE_KEY_M,           //!< 'M' key.
    CE_KEY_N,           //!< 'N' key.
    CE_KEY_O,           //!< 'O' key.
    CE_KEY_P,           //!< 'P' key.
    CE_KEY_Q,           //!< 'Q' key.
    CE_KEY_R,           //!< 'R' key.
    CE_KEY_S,           //!< 'S' key.
    CE_KEY_T,           //!< 'T' key.
    CE_KEY_U,           //!< 'U' key.
    CE_KEY_V,           //!< 'V' key.
    CE_KEY_W,           //!< 'W' key.
    CE_KEY_X,           //!< 'X' key.
    CE_KEY_Y,           //!< 'Y' key.
    CE_KEY_Z,           //!< 'Z' key.
    CE_KEY_NUM0,        //!< Numpad 0 key.
    CE_KEY_NUM1,        //!< Numpad 1 key.
    CE_KEY_NUM2,        //!< Numpad 2 key.
    CE_KEY_NUM3,        //!< Numpad 3 key.
    CE_KEY_NUM4,        //!< Numpad 4 key.
    CE_KEY_NUM5,        //!< Numpad 5 key.
    CE_KEY_NUM6,        //!< Numpad 6 key.
    CE_KEY_NUM7,        //!< Numpad 7 key.
    CE_KEY_NUM8,        //!< Numpad 8 key.
    CE_KEY_NUM9,        //!< Numpad 9 key.
    CE_KEY_F1,          //!< Function 1 key.
    CE_KEY_F2,          //!< Function 2 key.
    CE_KEY_F3,          //!< Function 3 key.
    CE_KEY_F4,          //!< Function 4 key.
    CE_KEY_F5,          //!< Function 5 key.
    CE_KEY_F6,          //!< Function 6 key.
    CE_KEY_F7,          //!< Function 7 key.
    CE_KEY_F8,          //!< Function 8 key.
    CE_KEY_F9,          //!< Function 9 key.
    CE_KEY_F10,         //!< Function 10 key.
    CE_KEY_F11,         //!< Function 11 key.
    CE_KEY_F12,         //!< Function 12 key.
    CE_KEY_NUMLOCK,     //!< Num Lock key.
    CE_KEY_SCROLLLOCK   //!< Scroll Lock key.
};

//! Describes the key which was pressed or released.
class KeyboardEvent
{
    KeyboardKey m_Key;      //!< The actual key number.
    bool        m_KeyDown;  //!< Indicates whether the key was pressed or released.
public:
    /*! \brief Constructor.
     *  \param key      the keyboard key enumerant.
     *  \param key_down indicates whether the key was pressed or released.
     */
    KeyboardEvent(KeyboardKey key, bool key_down)
        :   m_Key(key),
            m_KeyDown(key_down) {}

    //! Returns the key.
    KeyboardKey getKey() const { return m_Key; }

    //! Returns whether the key was pressed.
    bool isKeyDown() const { return m_KeyDown; }
};

//! Contains information about the current mouse position.
class MouseMotionEvent
{
    int m_XPos, //!< The x-coordinate component of the current mouse position.
        m_YPos, //!< The y-coordinate component of the current mouse position.
        m_XRel, //!< The difference between the previous and the current x-coordinate component of the mouse position.
        m_YRel; //!< The difference between the previous and the current y-coordinate component of the mouse position.
public:
    /*! \brief Constructor.
     *  \param _x   the current x-coordinate component of the mouse position.
     *  \param _y   the current y-coordinate component of the mouse position.
     *  \param xrel the difference between the current and the previous mouse position (x-coordinate component).
     *  \param yrel the difference between the current and the previous mouse position (y-coordinate component).
     */
    MouseMotionEvent(int _x, int _y, int xrel, int yrel)
        :   m_XPos(_x),
            m_YPos(_y),
            m_XRel(xrel),
            m_YRel(yrel) {}

    //! Returns the x-coordinate component of the current mouse position.
    int getX() const { return m_XPos; }

    //! Returns the y-coordinate component of the current mouse position.
    int getY() const { return m_YPos; }

    //! Returns the difference between the previous and the current x-coordinate component.
    int getRelX() const { return m_XRel; }

    //! Returns the difference between the previous and the current y-coordinate component.
    int getRelY() const { return m_YRel; }
};

//! Contains information about event triggered by the user when pressing the Quit button of the window.
class QuitEvent
{
public:
    //! Constructor.
    QuitEvent() {}
};

/*! \brief Raises an event and calls all functors associated with it.
 *  \tparam The type of the structure which contains the information carried by the event.
 *  \param evt_list     a list of all events associated with this event.
 *  \param arg          an object which contains information about the event.
 */
template<class T>
bool RaiseEvent(const DynamicArray< function<bool (const T&)> >& evt_list, const T& arg)
{
    bool res = false;
    for(size_t i = 0; i < evt_list.size(); ++i)
        res |= evt_list[i](arg);
    return res;
}

//! Opens an event list section.
#define EVENTS_LIST public

//! Declares an event which is described by the data structure given as an argument to this macro.
#define DECLARE_EVENT(name) \
    public: \
        typedef function<bool (const name&)> name##FType; \
        void subscribe##name(const name##FType& callback) { \
            __event_list_##name.push_back(callback); \
        } \
    private: \
        DynamicArray<name##FType> __event_list_##name;

/*! \brief Raises an event.
 *  \param obj      the object which has triggered the event.
 *  \param name     the structure/event.
 *  \param evt      the object which contains information about the event.
 */
#define RAISE_EVENT(obj, name, evt) \
    RaiseEvent(obj->__event_list_##name, evt)

/*! \brief Subscribe for the event.
 *  \param obj      the object which is going to inform about events associated with it.
 *  \param name     the name of the event/the structure representing the event.
 *  \param callback the function that must be called when the event gets triggered.
 */
#define SUBSCRIBE_EVENT(obj, name, callback) \
    (obj).subscribe##name(callback)

//! Serves the purpose of describing parameters about windows that are just being created.
struct WindowDescription
{
	//! The name of the window that is going to be created (application).
	string		name;
	size_t		width,		//!< The width in pixels of this window.
				height,		//!< The height in pixels of this window.
				samples;	//!< How many samples would be used for anti-aliasing(MSAA).
	WindowDescription() {}
	WindowDescription(string _name, size_t w, size_t h, size_t samp)
		:	name(_name),
			width(w),
			height(h),
			samples(samp) {}
};
}


#ifdef _WIN32
#include "window-w32.hh"
#elif defined(LINUX)
#include "window-x11.hh"
#else
#	error "Unsupported platform"
#endif

#endif /* CARINA_WINDOW_HH */
